package com.i72.basic;



import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.annotation.PostConstruct;


import com.i72.freeway.SpringContextHelper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import ch.qos.logback.classic.Level;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.exceptions.JedisException;

/**
 * @title 
        * @description 
        * @author jiangj 
        * @createTime 2022/1/21 22:42 
 */
@Slf4j
public class CacheHelper {

    private static final String LOCK_SUCCESS         = "OK";
    private static final String SET_IF_NOT_EXIST     = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long   RELEASE_SUCCESS      = 1L;
    private static final int    DEFAULT_TIMEOUT      = 5000;
    private static final int    DEFAULT_INTERVAL     = 50;
    private static final byte[] lock                 = new byte[0];
    private static final String script               = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    //private static final Logger cacheLogger = LoggerBuilder.getLogger("CacheHelper");

    //private static final ThreadLocal<LogModel.Builder> threadLocal = new ThreadLocal<>();

    private final static String                   DEFAULT_SINGLE_KEY  = "redis";
    private final static String                   DEFAULT_CLUSTER_KEY = "redis-cluster";
    private final static String                   REDIS_SYS_CODE      = "redis";
    private final static Map<String, CacheHelper> instances           = new HashMap<>();
    /**
     * 初始化默认集群redis实例，ZK的key = redis-cluster
     */
    public static CacheHelper                     single              = new CacheHelper(DEFAULT_SINGLE_KEY, true);
    public static CacheHelper                     cluster             = new CacheHelper();

    static {
        instances.put(DEFAULT_SINGLE_KEY, single);
        instances.put(DEFAULT_CLUSTER_KEY, cluster);
    }

    private final String key;
    // 单机连接池
    private JedisPool jedisPool;
    // 集群
    private JedisCluster  jedisCluster;
    private StringBuilder localIp     = new StringBuilder();
    private boolean       isStandalone;
    private RedisConfig   redisConfig = new RedisConfig();
    private SOAServiceCenter soaServiceCenter = null;

    /**
     * 不传key默认构建集群redis
     */
    public CacheHelper() {
        this(DEFAULT_CLUSTER_KEY, false);
    }

    public CacheHelper(String key) {
        this(DEFAULT_CLUSTER_KEY, !key.contains("cluster"));
    }

    public CacheHelper(String key, boolean isStandalone) {
        this.key = key;
        this.isStandalone = isStandalone;
    }

    /**
     * 构建CacheHelper，不传type默认构建单机redis，传*cluster构建集群redis
     * @param key redis zk配置key，命名规则：*cluster:集群， 其他：单机
     * @return CacheHelper实例
     */
    public static CacheHelper get(String... key) {
        if (key == null || key.length > 1) {
            throw new IllegalArgumentException("type parameter is invalid");
        }
        CacheHelper cacheHelper;
        if (key.length == 1 && key[0] != null && key[0].contains("cluster")) {
            if (DEFAULT_CLUSTER_KEY.equals(key[0])) {
                cacheHelper = cluster;
            } else {
                cacheHelper = getInstances(key[0], () -> new CacheHelper(key[0], false));
            }
        } else {
            if((key==null || key.length==0) || DEFAULT_SINGLE_KEY.equals(key[0])){
            //if (ArrayHelper.isEmpty(key) || DEFAULT_SINGLE_KEY.equals(key[0])) {
                cacheHelper = single;
            } else {
                cacheHelper = getInstances(key[0], () -> new CacheHelper(key[0], true));
            }
        }
        return cacheHelper;
    }

    private static CacheHelper getInstances(String key, Supplier<CacheHelper> supplier) {
        if (!instances.containsKey(key)) {
            synchronized (lock) {
                if (!instances.containsKey(key)) {
                    CacheHelper cacheHelper = supplier.get();
                    instances.put(key, cacheHelper);
                    return cacheHelper;
                }
            }
        }
        return instances.get(key);
    }

    private void initConfig(){

        if(this.soaServiceCenter==null) {
            synchronized (lock) {
                if(this.soaServiceCenter==null) {
                    SOAServiceCenter soaServiceCenter = SpringContextHelper.getApplicationContext().getBean(SOAServiceCenter.class);

                    this.soaServiceCenter = soaServiceCenter;
                }
            }
        }
    }

    private void initRedis() {
        initConfig();
        if (!isStandalone) {
            throw new RuntimeException("cluster redis instance is not support jedis command.");
        }
        if (soaServiceCenter.getConfig(key) == null) {
            throw new RuntimeException("init redis pool fail: no redis configuration in zookeeper");
        }
        if (jedisPool == null) {
            synchronized (lock) {
                if (jedisPool == null) {
                    Map<String, String> param = redisConfigSubString(soaServiceCenter.getConfig(key));

                    JedisPoolConfig config = new JedisPoolConfig();
                    config.setMaxTotal(Integer.parseInt(param.get("maxtotal")));
                    config.setMaxIdle(Integer.parseInt(param.get("maxidle")));
                    config.setMaxWaitMillis(Integer.parseInt(param.get("maxwait")));
                    config.setTestOnBorrow(Boolean.parseBoolean(param.get("testonborrow")));
                    int connectTimeout = Integer.parseInt(StringUtils.defaultIfBlank(param.get("connectTimeout"), "200"));
                    int socketTimeout = Integer.parseInt(StringUtils.defaultIfBlank(param.get("socketTimeout"), "600"));
                    jedisPool = new JedisPool(config, param.get("host"), Integer.parseInt(param.get("port")), connectTimeout, socketTimeout, StringUtils.defaultIfBlank(param.get("password"), null),
                            Protocol.DEFAULT_DATABASE, null, false, null, null, null);
                    localIp.append(param.get("host")).append(":").append("port");
                    isStandalone = true;
                    log.debug("init redis pool finish, key: {}", key);
                }
            }
        }
    }

    /**
     * 集群redies 服务器注册
     * @author 陈嘉文
     * @since 2018/4/10
     **/
    private void initJedisCluster() {
        initConfig();
        if (isStandalone) {
            throw new RuntimeException("standalone redis instance is not support jedisCluster command.");
        }
        if (soaServiceCenter.getConfig(key) == null) {
            throw new RuntimeException("init redis cluster fail : no redis-cluster configuration in zookeeper.");
        }
        if (jedisCluster == null) {
            synchronized (lock) {
                if (jedisCluster == null) {
                    Set<HostAndPort> nodes = new HashSet<>();
                    Map<String, String> map = redisConfigSubString(soaServiceCenter.getConfig(key));
                    String[] redisHosts = map.get("host").split(";");
                    String[] re;
                    for (String host : redisHosts) {
                        re = host.split(":");
                        nodes.add(new HostAndPort(re[0], Integer.parseInt(re[1])));
                        localIp.append(re[0]).append(":").append(re[1]).append(";");
                    }
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxTotal(Integer.parseInt(map.get("maxtotal")));
                    poolConfig.setMaxIdle(Integer.parseInt(map.get("maxidle")));
                    poolConfig.setMinIdle(Integer.parseInt(map.get("minidle")));
                    poolConfig.setMaxWaitMillis(Integer.parseInt(map.get("maxwait")));
                    int connectTimeout = Integer.parseInt(StringUtils.defaultIfBlank(map.get("connectTimeout"), "200"));
                    int socketTimeout = Integer.parseInt(StringUtils.defaultIfBlank(map.get("socketTimeout"), "600"));
                    int maxAttempts = Integer.parseInt(StringUtils.defaultIfBlank(map.get("maxAttempts"), "3"));
                    jedisCluster = new PipelineCluster(nodes, connectTimeout, socketTimeout, maxAttempts, poolConfig);
                    log.debug("init redis cluster finish, key: {}", key);
                }
            }
        }
    }

    private Map<String, String> redisConfigSubString(String redisConfig) {
        Map<String, String> map = new HashMap<>();
        String[] str = redisConfig.split("\\|");
        for (String s : str) {
            map.put(s.split("=")[0], s.split("=")[1]);
        }
        return map;
    }

    /**
     * 执行redis命令
     * 会抛出所有异常，自行try catch处理异常
     * @param consumer lambda表达式
     * @param <T> 返回类型
     * @return 返回值
     *
     * <pre>
     * String result = single.execute(jedisCommands -> jedisCommands.get(key));
     * </pre>
     */
    public <T> T execute(Function<JedisCommands, T> consumer) {
        /*
        if (!redisConfig.redisSwitch) {
            threadLocal.remove();
            return null;
        }*/

        T result = null;
        String errorMsg = null;
        Jedis jedis = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            if (isStandalone) {
                if (jedisPool == null) {
                    initRedis();
                }
                jedis = jedisPool.getResource();
                result = consumer.apply(jedis);
            } else {
                if (jedisCluster == null) {
                    initJedisCluster();
                }
                result = consumer.apply(jedisCluster);
            }
        } catch (JedisException e) {
            errorMsg = e.getMessage();
            //Alarm.redisLeapArray.currentWindow().value().addExMsgCount(errorMsg, 1);
            throw e;
        } catch (Exception e) {
            errorMsg = e.getMessage();
            throw e;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
            //log(stopWatch, Thread.currentThread().getStackTrace()[2].getMethodName(), result, errorMsg);
        }
        return result;
    }

    /**
     * 单机执行redis命令
     * 会抛出所有异常，自行try catch处理异常
     * @param consumer lambda表达式
     * @param <T> 返回类型
     * @return 返回值
     *
     * <pre>
     * String result = single.jedisExecute(jedis -> jedis.get(key));
     * </pre>
     */
    public <T> T jedisExecute(Function<Jedis, T> consumer) {
        /*
        if (!redisConfig.redisSwitch) {
            threadLocal.remove();
            return null;
        }*/
        T result = null;
        if (jedisPool == null) {
            initRedis();
        }
        String errorMsg = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try (Jedis jedis = jedisPool.getResource()) {
            result = consumer.apply(jedis);
        } catch (JedisException e) {
            errorMsg = e.getMessage();
            //Alarm.redisLeapArray.currentWindow().value().addExMsgCount(errorMsg, 1);
            throw e;
        } catch (Exception e) {
            errorMsg = e.getMessage();
            throw e;
        } finally {
            //log(stopWatch, Thread.currentThread().getStackTrace()[2].getMethodName(), result, errorMsg);
        }
        return result;
    }

    /**
     * 集群执行redis命令
     * 会抛出所有异常，自行try catch处理异常
     * @param consumer lambda表达式
     * @param <T> 返回类型
     * @return 返回值
     *
     * <pre>
     * String result = cluster.jedisClusterExecute(jedisCluster -> jedisCluster.get(key));
     * </pre>
     */
    public <T> T jedisClusterExecute(Function<JedisCluster, T> consumer) {
        /*
        if (!redisConfig.redisSwitch) {
            threadLocal.remove();
            return null;
        }*/

        T result = null;
        if (jedisCluster == null) {
            initJedisCluster();
        }
        String errorMsg = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            result = consumer.apply(jedisCluster);
        } catch (JedisException e) {
            errorMsg = e.getMessage();
            //Alarm.redisLeapArray.currentWindow().value().addExMsgCount(errorMsg, 1);
            throw e;
        } catch (Exception e) {
            errorMsg = e.getMessage();
            throw e;
        } finally {
            //log(stopWatch, Thread.currentThread().getStackTrace()[2].getMethodName(), result, errorMsg);
        }
        return result;
    }

    /**
     * 集群执行redis命令，批量处理
     * 会抛出所有异常，自行try catch处理异常
     * @param consumer lambda表达式
     * @param <T> 返回类型
     * @return 返回值
     *
     * <pre>
     * String result = cluster.pipelineClusterExecute(pipelineCluster -> pipelineCluster.mget(keys));
     * </pre>
     */
    public <T> T pipelineClusterExecute(Function<PipelineCluster, T> consumer) {
        /*
        if (!redisConfig.redisSwitch) {
            threadLocal.remove();
            return null;
        }*/

        T result = null;
        if (jedisCluster == null) {
            initJedisCluster();
        }
        String errorMsg = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            result = consumer.apply((PipelineCluster) jedisCluster);
        } catch (JedisException e) {
            errorMsg = e.getMessage();
            //Alarm.redisLeapArray.currentWindow().value().addExMsgCount(errorMsg, 1);
            throw e;
        } catch (Exception e) {
            errorMsg = e.getMessage();
            throw e;
        } finally {
            //log(stopWatch, Thread.currentThread().getStackTrace()[2].getMethodName(), result, errorMsg);
        }
        return result;
    }

    /**
     * 保存，过期时间若ZK没配置默认为600s
     * @param key key值
     * @param value value值
     */
    public void setValue(String key, String value) {
        setValue(key, value, redisConfig.expire);
    }

    /**
     * 批量保存，过期时间若ZK没配置默认为600s
     * @param keyValues key-value键值对
     */
    public void batchSetValue(Map<String, String> keyValues) {
        batchSetValue(keyValues, redisConfig.expire);
    }

    /**
     * 保存String
     * @param key key值
     * @param value value值
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public void setValue(String key, String value, Integer second) {
        if (StringUtils.isAnyBlank(key, value)) {
            return;
        }
        //startLog(key, value, second);
        try {
            execute(jedisCommands -> jedisCommands.setex(key, second, value));
        } catch (Exception e) {
            log.error("setValue failure", e);
        }
    }

    /**
     * 批量保存
     * @param keyValues key-value键值对
     * @param second 过期时间，单位s
     */
    public void batchSetValue(Map<String, String> keyValues, Integer second) {
        if(keyValues==null || keyValues.size()<=0){
        //if (MapUtils.isEmpty(keyValues)) {
            return;
        }
        //startLog(keyValues, second);
        try {
            if (isStandalone) {
                jedisExecute((jedis -> {
                    Pipeline pipeline = jedis.pipelined();
                    for (Map.Entry<String, String> entry : keyValues.entrySet()) {
                        pipeline.setex(entry.getKey(), second, entry.getValue());
                    }
                    List<Object> subResultList = pipeline.syncAndReturnAll();
                    if (subResultList == null || subResultList.size() != keyValues.size()) {
                        throw new JedisException(
                                "pipeline request keys and return list mismatched: request keys: " + keyValues.keySet() + ", return list: " + subResultList);
                    }
                    return null;
                }));
            } else {
                pipelineClusterExecute(pipelineCluster -> pipelineCluster.mset(keyValues, second));
            }
        } catch (Exception e) {
            log.error("batchSetValue failure", e);
        }
    }

    /**
     * 判断是否存在
     * @param key key值
     * @return true: 存在 false: 不存在
     */
    public boolean exists(String key) {
        if (StringUtils.isBlank(key)) {
            return false;
        }
        //startLog(key);
        try {
            return ObjectUtils.defaultIfNull(execute(jedisCommands -> jedisCommands.exists(key)), false);
        } catch (Exception e) {
            log.error("exists failure", e);
        }
        return false;
    }

    /**
     * 保存，对象自动转成JSON保存
     * @param key key值
     * @param obj value值
     * @param second 过期时间，单位为s
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public void setJsonValue(String key, Object obj, Integer second) {
        setValue(key, JSON.toJSONString(obj), second);
    }

    /**
     * 批量保存，对象自动转成JSON保存
     * @param keyObjects key-object map
     * @param second 过期时间，单位为s
     */
    public <T> void batchSetJsonValue(Map<String, T> keyObjects, Integer second) {
        if(keyObjects==null || keyObjects.size()<=0){
        //if (MapUtils.isEmpty(keyObjects)) {
            return;
        }
        //startLog(keyObjects, second);
        try {
            if (isStandalone) {
                jedisExecute((jedis -> {
                    Pipeline pipeline = jedis.pipelined();
                    for (Map.Entry<String, T> entry : keyObjects.entrySet()) {
                        pipeline.setex(entry.getKey(), second, JSON.toJSONString(entry.getValue()));
                    }
                    List<Object> subResultList = pipeline.syncAndReturnAll();
                    if (subResultList == null || subResultList.size() != keyObjects.size()) {
                        throw new JedisException(
                                "pipeline request keys and return list mismatched: request keys: " + keyObjects.keySet() + ", return list: " + subResultList);
                    }
                    return null;
                }));
            } else {
                pipelineClusterExecute(pipelineCluster -> {
                    Map<String, String> map = new HashMap<>();
                    for (Map.Entry<String, T> entry : keyObjects.entrySet()) {
                        map.put(entry.getKey(), JSON.toJSONString(entry.getValue()));
                    }
                    return pipelineCluster.mset(map, second);
                });
            }
        } catch (Exception e) {
            log.error("batchSetJsonValue failure", e);
        }
    }

    /**
     * 保存，对象自动转成JSON保存
     * @param key key值
     * @param obj value值
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public void setJsonValue(String key, Object obj) {
        setJsonValue(key, obj, redisConfig.expire);
    }

    /**
     * 批量保存，对象自动转成JSON保存
     * @param keyObjects key-object map
     */
    public <T> void batchSetJsonValue(Map<String, T> keyObjects) {
        batchSetJsonValue(keyObjects, redisConfig.expire);
    }

    /**
     * 根据key获取
     * @param key key
     * @return String
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public String getValue(String key) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        //startLog(key);
        try {
            return execute(jedisCommands -> jedisCommands.get(key));
        } catch (Exception e) {
            log.error("getValue failure", e);
        }
        return null;
    }

    /**
     * 批量获取
     * @param keys key集合
     * @return Map<String, String> 不会存在key有值，value=null的情况
     */
    public Map<String, String> batchGetValue(List<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return null;
        }
        //startLog(keys);
        try {
            Map<String, String> result = new LinkedHashMap<>();
            if (isStandalone) {
                return jedisExecute((jedis -> {
                    List<String> resultList = jedis.mget(keys.toArray(new String[] {}));
                    for (int i = 0; i < keys.size(); i++) {
                        String value = resultList.get(i);
                        if (value != null) {
                            result.put(keys.get(i), resultList.get(i));
                        }
                    }
                    return result;
                }));
            } else {
                return pipelineClusterExecute(pipelineCluster -> {
                    Map<String, String> map = pipelineCluster.mget(keys);
                    for (String key : keys) {
                        String value = map.get(key);
                        if (value != null) {
                            result.put(key, map.get(key));
                        }
                    }
                    return result;
                });
            }
        } catch (Exception e) {
            log.error("batchGetValue failure", e);
        }
        return null;
    }

    /**
     * 根据key获取对象，自动JSON反序列化
     * @param key key值
     * @param clz 类对象
     * @param <T> {@code T}
     * @return {@code <T> T}
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public <T> T getJsonObject(String key, Class<T> clz) {
        return JSON.parseObject(getValue(key), clz);
    }

    /**
     * 批量获取对象，自动JSON反序列化
     * @param keys key集合
     * @param clazz 类对象
     * @param <T> {@code T}
     * @return {@code <T> T}
     */
    public <T> Map<String, T> batchGetJsonObject(List<String> keys, Class<T> clazz) {
        if (CollectionUtils.isEmpty(keys)) {
            return null;
        }
        //startLog(keys);
        try {
            Map<String, T> result = new LinkedHashMap<>();
            if (isStandalone) {
                return jedisExecute((jedis -> {
                    List<String> resultList = jedis.mget(keys.toArray(new String[] {}));
                    for (int i = 0; i < keys.size(); i++) {
                        String value = resultList.get(i);
                        if (value != null) {
                            result.put(keys.get(i), JSON.parseObject(resultList.get(i), clazz));
                        }
                    }
                    return result;
                }));
            } else {
                return pipelineClusterExecute(pipelineCluster -> {
                    Map<String, String> map = pipelineCluster.mget(keys);
                    for (String key : keys) {
                        String value = map.get(key);
                        if (value != null) {
                            result.put(key, JSON.parseObject(map.get(key), clazz));
                        }
                    }
                    return result;
                });
            }
        } catch (Exception e) {
            log.error("batchGetJsonObject failure", e);
        }
        return null;
    }

    /**
     * 根据key获取对象，自动JSON反序列化
     * 如果结果返回空，在defaultResult可以定制返回的默认结果
     * @param key key值
     * @param clz 类对象
     * @param <T> {@code T}
     * @return {@code <T> T}
     * @author 陈嘉文
     * @since 2018/3/15
     */
    public <T> T getJsonObject(String key, Class<T> clz, Supplier<T> defaultResult) {
        T object = JSON.parseObject(getValue(key), clz);
        if (object != null) {
            return object;
        }
        return defaultResult.get();
    }

    /**
     * 删除，支持批量删除
     * @param keys 多个key值
     */
    public void deleteCache(String... keys) {
        if (ArrayUtils.isEmpty(keys)) {
            return;
        }
        //startLog(keys);
        try {
            if (isStandalone) {
                jedisExecute((jedis -> jedis.del(keys)));
            } else {
                if (keys.length == 1) {
                    jedisClusterExecute((jedisCluster) -> jedisCluster.del(keys));
                } else {
                    pipelineClusterExecute((pipelineCluster) -> pipelineCluster.mdel(Arrays.asList(keys)));
                }
            }
        } catch (Exception e) {
            log.error("deleteCache failure", e);
        }
    }

    /**
     * ！！需要处理异常，避免出现异常获取锁失败的情况！！
     * 尝试获取分布式锁，锁的过期时间5s，获取锁的间隔时间50ms，获取锁的超时时间5s
     * @param lockKey 键
     * @param requestId 请求标识，哪个请求的锁需由哪个请求去释放，可以使用UUID生成
     * @return 是否获取成功
     */
    public boolean acquire(String lockKey, String requestId) {
        return acquire(lockKey, requestId, DEFAULT_TIMEOUT, DEFAULT_INTERVAL, DEFAULT_TIMEOUT);
    }

    /**
     * 尝试获取分布式锁，锁的过期时间5s，获取锁的间隔时间50ms，获取锁的超时时间5s
     * @param lockKey 键
     * @param requestId 请求标识，哪个请求的锁需由哪个请求去释放，可以使用UUID生成
     * @return LockStat锁状态
     */
    public LockStat acquireLockStat(String lockKey, String requestId) {
        return acquireLockStat(lockKey, requestId, DEFAULT_TIMEOUT, DEFAULT_INTERVAL, DEFAULT_TIMEOUT);
    }

    /**
     * 尝试获取分布式锁
     * @param lockKey 键
     * @param expireTime 锁的过期时间，单位milliseconds
     * @param requestId 请求标识，哪个请求的锁需由哪个请求去释放，可以使用UUID生成
     * @param interval 获取锁的间隔时间，单位milliseconds
     * @param timeout 获取锁的超时时间，单位milliseconds
     * @return LockStat锁状态
     */
    public LockStat acquireLockStat(String lockKey, String requestId, int expireTime, int interval, int timeout) {
        try {
            if (!redisConfig.redisSwitch) {
                return LockStat.UNKNOWN;
            }
            return LockStat.valueOf(Boolean.valueOf(acquire(lockKey, requestId, expireTime, interval, timeout)).toString().toUpperCase());
        } catch (Exception e) {
            log.error("acquireLockStat failure", e);
            return LockStat.UNKNOWN;
        }
    }

    /**
     * ！！需要处理异常，避免出现异常获取锁失败的情况！！
     * 尝试获取分布式锁
     * @param lockKey 键
     * @param expireTime 锁的过期时间，单位milliseconds
     * @param requestId 请求标识，哪个请求的锁需由哪个请求去释放，可以使用UUID生成
     * @param interval 获取锁的间隔时间，单位milliseconds
     * @param timeout 获取锁的超时时间，单位milliseconds
     * @return 是否获取成功
     */
    public boolean acquire(String lockKey, String requestId, int expireTime, int interval, int timeout) {
        long start = System.currentTimeMillis();
        //startLog(lockKey, requestId, expireTime, interval, timeout);
        return ObjectUtils.defaultIfNull(execute((command) -> {
            boolean result = false;
            while (!result && System.currentTimeMillis() - start < timeout) {
                result = LOCK_SUCCESS.equals(command.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime));
                if (!result) {
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return result;
        }), false);
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识，哪个请求的锁需由哪个请求去释放，可以使用UUID生成
     * @return 是否释放成功
     */
    public boolean release(String lockKey, String requestId) {
        //startLog(lockKey, requestId);
        try {
            if (isStandalone) {
                return ObjectUtils.defaultIfNull(jedisExecute((command) -> {
                    Object result = command.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
                    return RELEASE_SUCCESS.equals(result);
                }), false);
            } else {
                return ObjectUtils.defaultIfNull(jedisClusterExecute((command) -> {
                    Object result = command.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
                    return RELEASE_SUCCESS.equals(result);
                }), false);
            }
        } catch (Exception e) {
            log.error("release failure", e);
            return false;
        }
    }


    public enum LockStat {
        /**
         * 获取redis锁成功
         */
        TRUE,
        /**
         * 获取redis锁失败
         */
        FALSE,
        /**
         * 获取redis锁未知，出现异常的情况或者redis开关关闭的情况
         */
        UNKNOWN
    }



    @Getter
    @Setter
    @ToString
    private static class RedisConfig {
        @JSONField(name = "switch")
        private boolean    redisSwitch  = true;
        private int        expire       = 600;
        //private SwitchEnum parameterLog = SwitchEnum.ON;
    }


}
